package hamaster.jpbc.web;

import static hamaster.jpbc.crypt.aes.Rijndael.AES_BLOCK_SIZE;
import static hamaster.jpbc.pbc.PBC.PBC_G_LENGTH;
import hamaster.jpbc.crypt.aes.AES;
import hamaster.jpbc.crypt.aes.OperationMode;
import hamaster.jpbc.mgr.ClientRequestManager;
import hamaster.jpbc.pbc.CipherText;
import hamaster.jpbc.pbc.PBC;
import hamaster.jpbc.pbc.PBCDriver;
import hamaster.jpbc.pbc.PlainText;
import hamaster.jpbc.pbc.PrivateKey;
import hamaster.jpbc.sys.IBCSystem;
import it.unisa.dia.gas.jpbc.Element;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Date;
import java.util.Properties;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Servlet implementation class ClientServlet
 */
public class ClientServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	private ClientRequestManager requestManager;
	private Logger logger = LoggerFactory.getLogger(getClass());

	/**
     * @see HttpServlet#HttpServlet()
     */
    public ClientServlet() {
    	requestManager = (ClientRequestManager) IBCSystem.getBean("clientRequestManager");
    }

	/*
	 * (non-Javadoc)
	 * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String action = request.getParameter("action");
		if (action == null || action.length() < 3)
			return;
		if ("getpp".equalsIgnoreCase(action)) {
			// TODO 返回公共参数
		}
		if ("getsp".equalsIgnoreCase(action)) {
			// TODO 返回签名参数
		}
		if ("client".equalsIgnoreCase(action)) {
			// 客户端请求操作
			String ustr = request.getParameter("u");
			String vstr = request.getParameter("v");
			String wstr = request.getParameter("w");
			String req = request.getParameter("req");
			String lstr = request.getParameter("l");
			if (ustr == null || vstr == null || wstr == null || req == null || lstr == null) {
				return;
			}
			logger.info("action:" + action + " @" + new Date());
			PBC pbc = PBC.instance();
			Element u = pbc.getPairing().getG1().newElement();
			Element v = pbc.getPairing().getGT().newElement();
			Element w = pbc.getPairing().getGT().newElement();
			u.setFromBytes(PBCDriver.unhex(ustr));
			v.setFromBytes(PBCDriver.unhex(vstr));
			w.setFromBytes(PBCDriver.unhex(wstr));
			CipherText c = new CipherText();
			c.setU(u);
			c.setV(v);
			c.setW(w);
			c.setLength(48);
//			Properties wangyeee = new Properties();
//			wangyeee.load(new FileInputStream(new File("F:\\wangyeee.id")));
//			PrivateKey pk = new PrivateKey(wangyeee , "pk");
			// TODO server id
			PrivateKey pk = IBCSystem.serverPrivateKey();

			PlainText sessKey = pbc.decrypt(c, pk);
			byte[] sessTmp = sessKey.getElement().toBytes();
			byte[] iv = new byte[AES_BLOCK_SIZE];
			byte[] key = new byte[32];
			System.arraycopy(sessTmp, PBC_G_LENGTH - sessKey.getLength(), iv, 0, AES_BLOCK_SIZE);
			System.arraycopy(sessTmp, PBC_G_LENGTH - sessKey.getLength() + AES_BLOCK_SIZE, key, 0, 32);
			byte[] skdec = new byte[48];	// 保存一份加密密钥
			System.arraycopy(iv, 0, skdec, 0, AES_BLOCK_SIZE);
			System.arraycopy(key, 0, skdec, AES_BLOCK_SIZE, 32);
			byte[] reqEnc = PBCDriver.unhex(req);
			AES decryptor = new AES();
			decryptor.setBlockSize(reqEnc.length);
			decryptor.setKey(key);
			decryptor.setInitializationVector(iv);
			decryptor.setOperationMode(OperationMode.CBC);
			byte[] reqDec = decryptor.decrypt(reqEnc);
			ByteArrayInputStream in = new ByteArrayInputStream(reqDec, 0, Integer.parseInt(lstr));
			Properties requestDec = new Properties();
			requestDec.load(in);
			Properties serverResponse = requestManager.processRequest(requestDec);
			ByteArrayOutputStream buffer = new ByteArrayOutputStream();
			serverResponse.store(buffer, null);
			byte[] data = buffer.toByteArray();
			int paddingSize;
			if (data.length % AES_BLOCK_SIZE == 0)
				paddingSize = data.length;
			else
				paddingSize = data.length - (data.length % AES_BLOCK_SIZE) + AES_BLOCK_SIZE;
			byte[] aesData = new byte[paddingSize];
			Arrays.fill(aesData, (byte) 0);
			System.arraycopy(data, 0, aesData, 0, data.length);
			byte[] iv0 = new byte[AES_BLOCK_SIZE];
			byte[] key0 = new byte[32];
			System.arraycopy(skdec, 0, iv0, 0, AES_BLOCK_SIZE);
			System.arraycopy(skdec, AES_BLOCK_SIZE, key0, 0, 32);
			AES encryptor = new AES();
			encryptor.setKey(key0);
			encryptor.setInitializationVector(iv0);
			encryptor.setOperationMode(OperationMode.CBC);
			encryptor.setBlockSize(paddingSize);
			byte[] responseContent = encryptor.encrypt(aesData);
			OutputStream out = response.getOutputStream();
			ByteBuffer b = ByteBuffer.allocate(4);
			b.putInt(data.length);
			out.write(b.array());
			out.write("\r\n".getBytes());
			out.write(responseContent);
			out.flush();
			out.close();
		}
	}
}
